`include "mycpu.h"
module wb_stage(
    input clk,
    input reset,
    //WB allowin
    output ws_allowin,
    //MEM to WB
    input ms_to_ws_valid,
    input [`MS_TO_WS_WD - 1:0] ms_to_ws_bus,
    //WB to ID
    output [`WS_TO_RF_WD - 1:0] ws_to_rf_bus,
    output out_ws_valid,
    //task12,13:
    output exec_flush,
    output [31:0] ws_pc_gen_exec,
    //debug
    output [31:0] debug_wb_rf_pc,
    output [3:0] debug_wb_rf_we,
    output [4:0] debug_wb_rf_wnum,
    output [31:0] debug_wb_rf_wdata
);

reg ws_valid;
wire ws_ready_go;
reg [`MS_TO_WS_WD - 1:0] ms_to_ws_bus_r;
wire [31:0] ws_pc;
wire [31:0] ws_final_result;
wire [31:0] ws_rf_wdata;
wire [4:0] ws_dest;
wire ws_gr_we;
wire ws_rf_we;
//exp12
wire ws_is_priv;
wire ws_inst_csrrd;
wire ws_inst_csrwr;
wire ws_inst_csrxchg;
wire ws_inst_syscall;
wire [14:0] ws_ex_code;
wire ws_inst_ertn;
wire ws_csr_re, ws_csr_we;
wire [13:0] ws_csr_num_inst;
wire [13:0] ws_csr_num;
wire [31:0] ws_rj_value;
wire [31:0] ws_csr_wmask;
wire [31:0] ws_csr_rvalue;
wire [31:0] ws_csr_wdata;
wire [31:0] ws_ex_entry;
wire ws_wb_ex;
wire [5:0] ws_ecode;
wire [8:0] ws_esubcode;
//exp13:
wire ws_ex_adef, ws_ex_ine, ws_ex_ale;
wire [31:0] ws_ex_baddr;
wire ws_inst_brk;
wire ws_inst_rdcntid;
wire ws_inst_rdcntvl_w;
wire ws_inst_rdcntvh_w;
wire ws_has_int;
reg ws_has_int_r;

assign out_ws_valid = ws_valid;

assign ws_ready_go = 1'b1;

assign ws_allowin = !ws_valid || ws_ready_go;

always @(posedge clk) begin
    if(reset)
        ws_valid <= 1'b0;
    else if(exec_flush)
        ws_valid <= 1'b0;
    else if(ws_allowin)
        ws_valid <= ms_to_ws_valid;
end

always @(posedge clk) begin
    if(ws_allowin && ms_to_ws_valid) 
        ms_to_ws_bus_r <= ms_to_ws_bus;
end

assign {
    ws_ex_adef,
    ws_ex_ine,
    ws_ex_ale,
    ws_ex_baddr[31:0],
    ws_inst_brk,
    ws_inst_rdcntid,
    ws_inst_rdcntvl_w,
    ws_inst_rdcntvh_w,
    ws_ex_code[14:0],
    ws_rj_value[31:0],
    ws_csr_wdata[31:0],
    ws_inst_syscall,
    ws_inst_ertn,
    ws_inst_csrrd,
    ws_inst_csrwr,
    ws_inst_csrxchg,
    ws_csr_num_inst[13:0],
    ws_pc[31:0],
    ws_gr_we,
    ws_dest[4:0],
    ws_final_result[31:0] } = ms_to_ws_bus_r[`MS_TO_WS_WD - 1:0];


//WB to RF
assign ws_rf_we = ws_gr_we & ws_valid & ~ws_wb_ex;

assign ws_to_rf_bus[`WS_TO_RF_WD - 1:0] = {
    ws_rf_we,               //37:37
    ws_dest[4:0],           //36:32
    ws_rf_wdata[31:0]   //31:0
};
                
assign ws_rf_wdata = ws_is_priv ? ws_csr_rvalue[31:0] : ws_final_result[31:0];

assign ws_is_priv = ws_inst_csrrd | ws_inst_csrwr | ws_inst_csrxchg | ws_inst_ertn
                |   ws_inst_syscall | ws_inst_brk | ws_inst_rdcntid | ws_inst_rdcntvh_w
                |   ws_inst_rdcntvl_w;

assign ws_csr_re = (ws_inst_csrrd 
                  | ws_inst_csrxchg 
                  | ws_inst_csrwr
                  | ws_inst_rdcntid
                  | ws_inst_rdcntvl_w
                  | ws_inst_rdcntvh_w) & ws_valid;
assign ws_csr_we = (ws_inst_csrwr | ws_inst_csrxchg) & ws_valid;

assign ws_ecode = {6{ws_valid & ws_has_int}} & `ECODE_INT
                | {6{ws_valid & ws_ex_adef}} & `ECODE_ADE
                | {6{ws_valid & ws_ex_ale}} & `ECODE_ALE
                | {6{ws_valid & ws_inst_syscall}} & `ECODE_SYS
                | {6{ws_valid & ws_inst_brk}} & `ECODE_BRK
                | {6{ws_valid & ws_ex_ine}} & `ECODE_INE;
                
assign ws_esubcode = 9'h000;

assign ws_wb_ex = (ws_inst_syscall 
                 | ws_inst_brk 
                 | ws_has_int
                 | ws_ex_adef
                 | ws_ex_ine
                 | ws_ex_ale) & ws_valid;

assign exec_flush = (ws_inst_syscall 
                   | ws_inst_ertn
                   | ws_inst_brk
                   | ws_ex_adef
                   | ws_ex_ine
                   | ws_ex_ale
                   | ws_has_int) & ws_valid;

assign ws_pc_gen_exec = {32{ws_inst_ertn}} & ws_csr_rvalue
                    |   {32{exec_flush & ~ws_inst_ertn}} & ws_ex_entry;

assign ws_csr_num = {14{ws_inst_ertn}} & `CSR_ERA
                 |  {14{ws_inst_rdcntvl_w | ws_inst_rdcntvh_w}} & `CSR_TVAL
                 |  {14{ws_inst_rdcntid}} & `CSR_TID
                 |  {14{~ws_inst_ertn & ~ws_inst_rdcntid & ~ws_inst_rdcntvl_w
                     &  ~ws_inst_rdcntvh_w}} & ws_csr_num_inst;

assign ws_csr_wmask = ws_inst_csrxchg ? ws_rj_value : 32'hffffffff;

csr u_csr(
    .clk(clk),
    .reset(reset),
    .csr_re(ws_csr_re),
    .csr_we(ws_csr_we),
    .csr_num(ws_csr_num),
    .csr_rvalue(ws_csr_rvalue),
    .csr_wmask(ws_csr_wmask),
    .csr_wvalue(ws_csr_wdata),
    .wb_ex(ws_wb_ex),
    .wb_pc(ws_pc),
    .wb_ecode(ws_ecode),
    .wb_esubcode(ws_esubcode),
    .wb_vaddr(ws_ex_baddr),
    .ertn_flush(ws_inst_ertn),
    .has_int(ws_has_int),
    .ex_entry(ws_ex_entry),
    .hw_int_in(8'b0),
    .ipi_int_in(1'b0),
    .coreid_in(32'b0)
);

assign debug_wb_rf_pc = ws_pc;
assign debug_wb_rf_we = {4{ws_rf_we}};
assign debug_wb_rf_wnum = ws_dest;
assign debug_wb_rf_wdata = ws_rf_wdata;

endmodule
